﻿// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

using DocumentFormat.OpenXml.Framework;
using System;
using System.Xml;

namespace DocumentFormat.OpenXml
{
    /// <summary>
    /// Defines the XmlConvertingReader - This XmlReader tries to replace the Strict namespaces with equivalent Transitional namespaces.
    /// </summary>
    internal class XmlConvertingReader : XmlReader, IXmlLineInfo
    {
        /// <summary>
        /// Creates an instance of <see cref="XmlConvertingReader"/>
        /// </summary>
        /// <param name="baseReader">XmlReader</param>
        /// <param name="strictRelationshipFound">bool</param>
        public XmlConvertingReader(XmlReader baseReader, bool strictRelationshipFound)
        {
            BaseReader = baseReader ?? throw new ArgumentNullException(nameof(baseReader));
            StrictRelationshipFound = strictRelationshipFound;
        }

        /// <summary>
        /// Gets the inner <see cref="XmlReader"/>
        /// </summary>
        protected XmlReader BaseReader { get; }

        /// <summary>
        /// Gets a value indicating whether strict translation is enabled.
        /// </summary>
        public bool StrictRelationshipFound { get; }

#if FEATURE_ABSTRACT_XML_CLOSE
        /// <inheritdoc/>
        public override void Close() => BaseReader.Close();
#endif

        /// <inheritdoc/>
        protected override void Dispose(bool disposing)
        {
            base.Dispose(disposing);

#if FEATURE_XML_DISPOSE_PROTECTED
            (BaseReader as IDisposable)?.Dispose();
#else
            BaseReader.Dispose();
#endif
        }

        /// <inheritdoc/>
        public override bool Read() => BaseReader.Read();

        /// <inheritdoc/>
        public override string GetAttribute(int index) => BaseReader.GetAttribute(index);

        /// <inheritdoc/>
        public override string? GetAttribute(string name) => BaseReader.GetAttribute(name);

        /// <inheritdoc/>
        public override string? GetAttribute(string localName, string? namespaceURI) => BaseReader.GetAttribute(localName, namespaceURI);

        /// <inheritdoc/>
        public override string? LookupNamespace(string prefix) => BaseReader.LookupNamespace(prefix);

        /// <inheritdoc/>
        public override void MoveToAttribute(int index) => BaseReader.MoveToAttribute(index);

        /// <inheritdoc/>
        public override bool MoveToAttribute(string name) => BaseReader.MoveToAttribute(name);

        /// <inheritdoc/>
        public override bool MoveToAttribute(string localName, string? namespaceURI) => BaseReader.MoveToAttribute(localName, namespaceURI);

        /// <inheritdoc/>
        public override bool MoveToElement() => BaseReader.MoveToElement();

        /// <inheritdoc/>
        public override bool MoveToFirstAttribute() => BaseReader.MoveToFirstAttribute();

        /// <inheritdoc/>
        public override bool MoveToNextAttribute() => BaseReader.MoveToNextAttribute();

        /// <inheritdoc/>
        public override bool ReadAttributeValue() => BaseReader.ReadAttributeValue();

        /// <inheritdoc/>
        public override void ResolveEntity() => BaseReader.ResolveEntity();

        /// <inheritdoc/>
        public override int ReadValueChunk(char[] buffer, int index, int count) => BaseReader.ReadValueChunk(buffer, index, count);

        /// <inheritdoc/>
        public override int AttributeCount => BaseReader.AttributeCount;

        /// <inheritdoc/>
        public override string? BaseURI => BaseReader.BaseURI;

        /// <inheritdoc/>
        public override bool CanReadBinaryContent => BaseReader.CanReadBinaryContent;

        /// <inheritdoc/>
        public override bool CanReadValueChunk => BaseReader.CanReadValueChunk;

        /// <inheritdoc/>
        public override bool CanResolveEntity => BaseReader.CanResolveEntity;

        /// <inheritdoc/>
        public override int Depth => BaseReader.Depth;

        /// <inheritdoc/>
        public override bool EOF => BaseReader.EOF;

        /// <inheritdoc/>
        public override bool HasValue => BaseReader.HasValue;

        /// <inheritdoc/>
        public override bool IsDefault => BaseReader.IsDefault;

        /// <inheritdoc/>
        public override bool IsEmptyElement => BaseReader.IsEmptyElement;

        /// <inheritdoc/>
        public override string this[int index] => BaseReader[index];

        /// <inheritdoc/>
        public override string? this[string name] => BaseReader[name];

        /// <inheritdoc/>
        public override string? this[string name, string? namespaceURI] => BaseReader[name, namespaceURI];

        /// <inheritdoc/>
        public override string LocalName => BaseReader.LocalName;

        /// <inheritdoc/>
        public override string Name => BaseReader.Name;

        /// <inheritdoc/>
        public override string NamespaceURI => ApplyStrictTranslation(BaseReader.NamespaceURI);

        /// <inheritdoc/>
        public override XmlNameTable NameTable => BaseReader.NameTable;

        /// <inheritdoc/>
        public override XmlNodeType NodeType => BaseReader.NodeType;

        /// <inheritdoc/>
        public override string Prefix => BaseReader.Prefix;

#if FEATURE_XML_QUOTECHAR
        /// <inheritdoc/>
        public override char QuoteChar => BaseReader.QuoteChar;
#endif

        /// <inheritdoc/>
        public override ReadState ReadState => BaseReader.ReadState;

        /// <inheritdoc/>
        public override string Value => BaseReader.NodeType == XmlNodeType.Attribute ? ApplyStrictTranslation(BaseReader.Value) : BaseReader.Value;

        /// <inheritdoc/>
        public override string XmlLang => BaseReader.XmlLang;

        /// <inheritdoc/>
        public override XmlSpace XmlSpace => BaseReader.XmlSpace;

        int IXmlLineInfo.LineNumber => XmlLineInfo.Get(BaseReader).LineNumber;

        int IXmlLineInfo.LinePosition => XmlLineInfo.Get(BaseReader).LinePosition;

        bool IXmlLineInfo.HasLineInfo() => XmlLineInfo.Get(BaseReader).HasLineInfo();

        private string ApplyStrictTranslation(in OpenXmlNamespace ns)
        {
            if (StrictRelationshipFound)
            {
                if (ns.TryGetTransitionalNamespace( out var transitionalNamespace))
                {
                    return transitionalNamespace.Uri;
                }
            }
            else if (ns.TryGetExtendedNamespace( out var extendedNamespace))
            {
                return extendedNamespace.Uri;
            }

            return ns.Uri;
        }
    }
}
